【AWS CDK】豊富なルールセットでコンプライアンスチェックができるcdk-nagを試してみた
先日 CDK Aspects を使ったデプロイ時のコンプライアンスチェックを行なってみました。
その際、cdk-nag というルールをまとめたパッケージが推奨されていたので確認してみます。
本記事では主に以下を中心に試していきます。
- cdk-nag の基本的な使い方
- エラーの修正と抑制
- 複数ルールセットの併用方法
cdk-nagとは
CDK でデプロイする際、Constructs がルールに適応しているか事前に検証してくれるツールです。
対応言語は以下の通りです。
- JavaScript、Typescript
- Python
- Java
- .NET
- Go
また、現時点では以下のルールセットが含まれています。必要なものを選択してがチェック可能です。
- AWS Solutions
- HIPAA Security
- NIST 800-53 rev 4
- NIST 800-53 rev 5
- および PCI DSS 3.2.1
詳細について以下を参照してください。
非常に多くのルールがあるため自前で CDK Aspects を使って用意するのは大変です。
用意されているルールセットを活用してコンプライアンス準拠を強制できるのは便利そうです。
やってみる
以下 AWS ブログを参考に実装してみます。
S3バケットの作成
CDK のセットアップをします。
mkdir cdk-nag-test
cd cdk-nag-test
cdk init app --language typescript
次に今回チェック対象とする S3 バケットを作成します。
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { Bucket } from "aws-cdk-lib/aws-s3";
export class CdkNagTestStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const bucket = new Bucket(this, "Bucket");
}
}
cdk-nag の導入
cdk-nag をインストールします。CDK のバージョンが低い場合エラーになるので、適宜アップデートしてください。
npm install cdk-nag
以下のように cdk-nag を使ったチェックをbin/cdk-nag-test.ts
に追加します。今回は AWS Solutions のルールでチェックしていきます。
Construct に適用できるので、app でまとめてチェックするようにしました。
#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { CdkNagTestStack } from "../lib/cdk-nag-test-stack";
import { AwsSolutionsChecks } from "cdk-nag";
import { Aspects } from "aws-cdk-lib";
import "source-map-support/register";
const app = new cdk.App();
Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }));
new CdkNagTestStack(app, "CdkNagTestStack", {});
これで導入完了です。npx cdk synth
を実行すると、2つのルールでエラーになっていることが確認できました。
❯ npx cdk synth
[Error at /CdkNagTestStack/Bucket/Resource] AwsSolutions-S1: The S3 Bucket has server access logs disabled. The bucket should have server access logging enabled to provide detailed records for the requests that are made to the bucket.
[Error at /CdkNagTestStack/Bucket/Resource] AwsSolutions-S10: The S3 Bucket or bucket policy does not require requests to use SSL. You can use HTTPS (TLS) to help prevent potential attackers from eavesdropping on or manipulating network traffic using person-in-the-middle or similar attacks. You should allow only encrypted connections over HTTPS (TLS) using the aws:SecureTransport condition on Amazon S3 bucket policies.
Found errors
cdk.out
の配下にAwsSolutions-CdkNagTestStack-NagReport.csv
というレポートが出力されるため、こちらも確認してみます。
Rule ID,Resource ID,Compliance,Exception Reason,Rule Level,Rule Info
"AwsSolutions-S1","CdkNagTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket has server access logs disabled."
"AwsSolutions-S2","CdkNagTestStack/Bucket/Resource","Compliant","N/A","Error","The S3 Bucket does not have public access restricted and blocked."
"AwsSolutions-S5","CdkNagTestStack/Bucket/Resource","Compliant","N/A","Error","The S3 static website bucket either has an open world bucket policy or does not use a CloudFront Origin Access Identity (OAI) in the bucket policy for limited getObject and/or putObject permissions."
"AwsSolutions-S10","CdkNagTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket or bucket policy does not require requests to use SSL."
Aws Solutions のルールから S3 バケットに対してチェックされた結果が出力されているようです。今回はAwsSolutions-S1
とAwsSolutions-S10
がエラーとなっていたので、Non-Compliant
になっていますね。他は問題ないためCompliant
になっています。
チェックされた内容を CSV の形で確認できるのは嬉しいですね。
エラーの修正と抑制
2つのエラーが出ているため、これらを出ないように修正と抑制をします。
まずはAwsSolutions-S1
を抑制してみましょう。
抑制するには以下のように書き換えてください。
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { Bucket } from "aws-cdk-lib/aws-s3";
+import { NagSuppressions } from "cdk-nag";
export class CdkNagTestStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
+ NagSuppressions.addStackSuppressions(this, [
+ {
+ id: "AwsSolutions-S1",
+ reason: "Demonstrate a stack level suppression.",
+ },
+ ]);
const bucket = new Bucket(this, "Bucket");
}
}
この状態で一度確認すると、AwsSolutions-S1 が抑制されたことが確認できます。
❯ npx cdk synth
[Error at /CdkNagTestStack/Bucket/Resource] AwsSolutions-S10: The S3 Bucket or bucket policy does not require requests to use SSL. You can use HTTPS (TLS) to help prevent potential attackers from eavesdropping on or manipulating network traffic using person-in-the-middle or similar attacks. You should allow only encrypted connections over HTTPS (TLS) using the aws:SecureTransport condition on Amazon S3 bucket policies.
Found errors
レポートを確認すると、AwsSolutions-S1
の Compliance がSuppressed
となっていることが確認できます。
Rule ID,Resource ID,Compliance,Exception Reason,Rule Level,Rule Info
"AwsSolutions-S1","CdkNagTestStack/Bucket/Resource","Suppressed","Demonstrate a stack level suppression.","Error","The S3 Bucket has server access logs disabled."
"AwsSolutions-S2","CdkNagTestStack/Bucket/Resource","Compliant","N/A","Error","The S3 Bucket does not have public access restricted and blocked."
"AwsSolutions-S5","CdkNagTestStack/Bucket/Resource","Compliant","N/A","Error","The S3 static website bucket either has an open world bucket policy or does not use a CloudFront Origin Access Identity (OAI) in the bucket policy for limited getObject and/or putObject permissions."
"AwsSolutions-S10","CdkNagTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket or bucket policy does not require requests to use SSL."
最後にAwsSolutions-S10
のエラーを解消するため、S3 バケットに SSL/TLS 通信を強制するように修正します。
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { Bucket } from "aws-cdk-lib/aws-s3";
import { NagSuppressions } from "cdk-nag";
export class CdkNagTestStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
NagSuppressions.addStackSuppressions(this, [
{
id: "AwsSolutions-S1",
reason: "Demonstrate a stack level suppression.",
},
]);
- const bucket = new Bucket(this, "Bucket");
+ const bucket = new Bucket(this, "Bucket", {
+ enforceSSL: true,
+ });
}
}
npx cdk synth
を実行してみると、cdk-nag のエラーが発生せずに成功しました。
これで無事にエラーの修正と抑制が確認できました。
他のルール適用
Aws Solutions のルールでチェックしましたが、他のルールを適用してみます。
HIPAA Security に変更してみました。
#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { CdkNagTestStack } from "../lib/cdk-nag-test-stack";
- import { AwsSolutionsChecks } from "cdk-nag";
+ import { HIPAASecurityChecks } from "cdk-nag";
import { Aspects } from "aws-cdk-lib";
import "source-map-support/register";
const app = new cdk.App();
- Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }));
+ Aspects.of(app).add(new HIPAASecurityChecks({ verbose: true }));
new CdkNagTestStack(app, "CdkNagTestStack", {});
エラーを確認してみます。
❯ npx cdk synth
[Error at /CdkNagTestStack/Bucket/Resource] HIPAA.Security-S3BucketLoggingEnabled: The S3 Bucket does not have server access logs enabled - (Control IDs: 164.308(a)(3)(ii)(A), 164.312(b)). Amazon Simple Storage Service (Amazon S3) server access logging provides a method to monitor the network for potential cybersecurity events. The events are monitored by capturing detailed records for the requests that are made to an Amazon S3 bucket. Each access log record provides details about a single access request. The details include the requester, bucket name, request time, request action, response status, and an error code, if relevant.
[Error at /CdkNagTestStack/Bucket/Resource] HIPAA.Security-S3BucketPublicReadProhibited: The S3 Bucket does not prohibit public read access through its Block Public Access configurations and bucket ACLs - (Control IDs: 164.308(a)(3)(i), 164.308(a)(4)(ii)(A), 164.308(a)(4)(ii)(C), 164.312(a)(1), 164.312(e)(1)). The management of access should be consistent with the classification of the data.
[Error at /CdkNagTestStack/Bucket/Resource] HIPAA.Security-S3BucketPublicWriteProhibited: The S3 Bucket does not prohibit public write access through its Block Public Access configurations and bucket ACLs - (Control IDs: 164.308(a)(3)(i), 164.308(a)(4)(ii)(A), 164.308(a)(4)(ii)(C), 164.312(a)(1), 164.312(e)(1)). The management of access should be consistent with the classification of the data.
[Error at /CdkNagTestStack/Bucket/Resource] HIPAA.Security-S3BucketReplicationEnabled: The S3 Bucket does not have replication enabled - (Control IDs: 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.308(a)(7)(ii)(B)). Amazon Simple Storage Service (Amazon S3) Cross-Region Replication (CRR) supports maintaining adequate capacity and availability. CRR enables automatic, asynchronous copying of objects across Amazon S3 buckets to help ensure that data availability is maintained.
[Error at /CdkNagTestStack/Bucket/Resource] HIPAA.Security-S3BucketVersioningEnabled: The S3 Bucket does not have versioning enabled - (Control IDs: 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.308(a)(7)(ii)(B), 164.312(c)(1), 164.312(c)(2)). Use versioning to preserve, retrieve, and restore every version of every object stored in your Amazon S3 bucket. Versioning helps you to easily recover from unintended user actions and application failures.
[Error at /CdkNagTestStack/Bucket/Resource] HIPAA.Security-S3DefaultEncryptionKMS: The S3 Bucket is not encrypted with a KMS Key by default - (Control IDs: 164.312(a)(2)(iv), 164.312(e)(2)(ii)). Ensure that encryption is enabled for your Amazon Simple Storage Service (Amazon S3) buckets. Because sensitive data can exist at rest in an Amazon S3 bucket, enable encryption at rest to help protect that data.
Found errors
チェック項目が異なるため、Aws Solutions で成功したコードでも HIPAA Security ではエラーとなりました。
また、Aws Solutions 同様レポートが出力されました。
Rule ID,Resource ID,Compliance,Exception Reason,Rule Level,Rule Info
"HIPAA.Security-S3BucketLevelPublicAccessProhibited","CdkNagTestStack/Bucket/Resource","Compliant","N/A","Error","The S3 bucket does not prohibit public access through bucket level settings - (Control IDs: 164.308(a)(3)(i), 164.308(a)(4)(ii)(A), 164.308(a)(4)(ii)(C), 164.312(a)(1), 164.312(e)(1))."
"HIPAA.Security-S3BucketLoggingEnabled","CdkNagTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket does not have server access logs enabled - (Control IDs: 164.308(a)(3)(ii)(A), 164.312(b))."
"HIPAA.Security-S3BucketPublicReadProhibited","CdkNagTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket does not prohibit public read access through its Block Public Access configurations and bucket ACLs - (Control IDs: 164.308(a)(3)(i), 164.308(a)(4)(ii)(A), 164.308(a)(4)(ii)(C), 164.312(a)(1), 164.312(e)(1))."
"HIPAA.Security-S3BucketPublicWriteProhibited","CdkNagTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket does not prohibit public write access through its Block Public Access configurations and bucket ACLs - (Control IDs: 164.308(a)(3)(i), 164.308(a)(4)(ii)(A), 164.308(a)(4)(ii)(C), 164.312(a)(1), 164.312(e)(1))."
"HIPAA.Security-S3BucketReplicationEnabled","CdkNagTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket does not have replication enabled - (Control IDs: 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.308(a)(7)(ii)(B))."
"HIPAA.Security-S3BucketSSLRequestsOnly","CdkNagTestStack/Bucket/Resource","Compliant","N/A","Error","The S3 Bucket or bucket policy does not require requests to use SSL - (Control IDs: 164.312(a)(2)(iv), 164.312(c)(2), 164.312(e)(1), 164.312(e)(2)(i), 164.312(e)(2)(ii))."
"HIPAA.Security-S3BucketVersioningEnabled","CdkNagTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket does not have versioning enabled - (Control IDs: 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.308(a)(7)(ii)(B), 164.312(c)(1), 164.312(c)(2))."
"HIPAA.Security-S3DefaultEncryptionKMS","CdkNagTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket is not encrypted with a KMS Key by default - (Control IDs: 164.312(a)(2)(iv), 164.312(e)(2)(ii))."
"HIPAA.Security-S3BucketSSLRequestsOnly","CdkNagTestStack/Bucket/Policy/Resource","Compliant","N/A","Error","The S3 Bucket or bucket policy does not require requests to use SSL - (Control IDs: 164.312(a)(2)(iv), 164.312(c)(2), 164.312(e)(1), 164.312(e)(2)(i), 164.312(e)(2)(ii))."
複数ルールの併用
あまり併用することはないかも知れませんが、複数ルールの併用も可能です。
#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { CdkNagTestStack } from "../lib/cdk-nag-test-stack";
import { AwsSolutionsChecks, HIPAASecurityChecks } from "cdk-nag";
import { Aspects } from "aws-cdk-lib";
import "source-map-support/register";
const app = new cdk.App();
Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }));
Aspects.of(app).add(new HIPAASecurityChecks({ verbose: true }));
new CdkNagTestStack(app, "CdkNagTestStack", {});
単純にインポートしてチェックしたい Construct に書くだけなので簡単ですね。
まとめ
cdk-nag を使ったコンプライアンスチェックを試してみました。
インストールしてから使うまでは非常にスムーズなので、導入のハードルが低いのはいいですね。レポートが出力されるのもコンプライアンスの準拠状況を可視化できるため有用そうです。
必要に応じてルールセットを選択できますし、デプロイ前にチェックできることから事故を防ぐ面でも非常に強力なツールです。
ぜひ採用してセキュリティを意識した開発をしていきましょう。